package com.hp.modules.sys.service.impl;

import com.hp.common.base.YesNoEnum;
import com.hp.common.config.GlobalProperties;
import com.hp.common.exception.BizException;
import com.hp.common.token.TokenStrategy;
import com.hp.common.tool.Md5Tool;
import com.hp.common.web.RequestHolder;
import com.hp.dao.SysUserDao;
import com.hp.mapper.SysDeptMapper;
import com.hp.mapper.SysUserLoginTimesMapper;
import com.hp.mapper.SysUserMapper;
import com.hp.modules.sys.dto.SysLoginParam;
import com.hp.modules.sys.entity.SysDept;
import com.hp.modules.sys.entity.SysUser;
import com.hp.modules.sys.entity.SysUserLoginTimes;
import com.hp.modules.sys.enums.SysErrEnum;
import com.hp.modules.sys.service.SysLoginService;
import com.hp.modules.sys.service.SysRbacService;
import com.hp.modules.sys.vo.SysLoginVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class SysLoginServiceImpl implements SysLoginService {

    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysUserLoginTimesMapper sysUserLoginTimesMapper;

    @Autowired
    private GlobalProperties globalProperties;
    @Autowired
    private TokenStrategy generateTokenStrategy;
    @Autowired
    private SysRbacService sysRbacService;
    @Autowired
    private SysUserDao sysUserDao;
    @Autowired
    private SysDeptMapper sysDeptMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysLoginVo login(SysLoginParam param) {
        String userName = param.getUserName();
        String password = param.getPassword();
        SysUser q = new SysUser();
        q.setUserName(userName);
        SysUser user = sysUserMapper.selectOne(q);
        if (null == user) {
            // 用户不存在
            throw new BizException(SysErrEnum.SYS80000001);
        }
        if (YesNoEnum.YES.equals(user.getIsLocked())) {
            //用户已被锁定
            throw new BizException(SysErrEnum.SYS80000004);
        }
        // 校验登录次数
        SysUserLoginTimes userLoginTimesQuery = new SysUserLoginTimes();
        userLoginTimesQuery.setUserId(user.getId());
        SysUserLoginTimes userLoginTimes = sysUserLoginTimesMapper.selectOne(userLoginTimesQuery);
        if (null == userLoginTimes ||
                (null != userLoginTimes && userLoginTimes.getTimes() <= globalProperties.getMaxErrLoginTimes())) {
            String passwordEncry = Md5Tool.md5(password, user.getSalt());
            if (!passwordEncry.equals(user.getPassword())) {
                // 用户名或者密码错误
                Date now = new Date();
                if (userLoginTimes == null) {
                    userLoginTimes = new SysUserLoginTimes();
                    userLoginTimes.setUserId(user.getId());
                    userLoginTimes.setCreateTime(now);
                    userLoginTimes.setUpdateTime(now);
                    userLoginTimes.setTimes(1);
                    sysUserLoginTimesMapper.insertSelective(userLoginTimes);
                } else {
                    userLoginTimes.setUserId(null);
                    userLoginTimes.setCreateTime(null);
                    userLoginTimes.setUpdateTime(now);
                    userLoginTimes.setTimes(userLoginTimes.getTimes() + 1);
                    sysUserLoginTimesMapper.updateByPrimaryKeySelective(userLoginTimes);
                }
                throw new BizException(SysErrEnum.SYS80000002);
            }
        } else {
            // 登录次数太多，账号已被锁定，请联系管理员
            throw new BizException(SysErrEnum.SYS80000003);
        }
        return createLoginVo(user);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int logout(String token) {
        generateTokenStrategy.removeToken(token);
        return 1;
    }

    /**
     * 创建登录返回vo
     *
     * @param user
     * @return
     */
    private SysLoginVo createLoginVo(SysUser user) {
        SysLoginVo vo = new SysLoginVo();
        Long userId = user.getId();
        String avatar = user.getAvatar();
        String userName = user.getUserName();
        String realName = user.getRealName();
        Map<String, Object> ext = new HashMap<>();
        ext.put("deptId", user.getDeptId() == null ? 0L : user.getDeptId());
        ext.put("childDeptIds", "");
        if (!RequestHolder.isSuperAdmin()) {
            ext.put("dataScope", sysUserDao.selectUserDataScope(user.getId()));
            if (user.getDeptId() != null) {
                List<SysDept> childDeptList = findChildDept(user.getDeptId());
                List<String> childDeptIds = childDeptList.stream().map(item -> {
                    return item.getId().toString();
                }).collect(Collectors.toList());
                ext.put("childDeptIds", String.join(",", childDeptIds));
            }
        }
        // 创建token
        String token = generateTokenStrategy.generateToken(userId, userName, ext);
        vo.setAccessList(sysRbacService.loadUserAccessList(userId));
        vo.setAvatar(avatar);
        vo.setMenuList(sysRbacService.loadUserMenuList(userId));
        vo.setRealName(realName);
        vo.setUserId(userId);
        vo.setUserName(userName);
        vo.setToken(token);
        return vo;
    }

    /**
     * 获取所有子部门
     *
     * @param parentId
     * @return
     */
    private List<SysDept> findChildDept(Long parentId) {
        List<SysDept> childDeptList = new ArrayList<>();
        SysDept q = new SysDept();
        q.setIsDeleted(YesNoEnum.NO);
        q.setIsEnabled(YesNoEnum.YES);
        q.setParentId(parentId);
        List<SysDept> deptList = sysDeptMapper.select(q);
        if (!deptList.isEmpty()) {
            childDeptList.addAll(deptList);
            deptList.forEach(item -> {
                childDeptList.addAll(findChildDept(item.getId()));
            });
        }
        return childDeptList;
    }
}
